// IA32NestedLoopDetector.cpp

//#include "../crossbit/VInst.cpp"
#include "crossbit/VBlockAnalyzer.h"
#include "arch/ia32/IA32VBasicBlockBuilder.h"
#include "arch/ia32/IA32Interpreter.h"
#include "arch/ia32/IA32NestedLoopDetector.h"
#include "disasm.h"

using namespace std;

namespace crossbit
{
	bool IA32NestedLoopDetector::__getBoundInfo(XTMemAddr entry, RegNum &reg, XTInt32 &off)
	{
		//printf("in getBoundInfo, entry = 0x%x\n", entry);
		bool status = true;
		IA32VBasicBlockBuilder builder;
		VBlockAnalyzer analyzer;
		VBlock *vb;
		XTMemAddr next = entry;
		builder.getReady(entry);
		interp->setMode(PTX);
		XTMemAddr pre = next;
		while( (!builder.isEnd()) && status )
		{
			// just a simple bug fix...don't know the reason...
			if(next < pre) { status = false; break; }
			pre = next;
			next = interp->decode(next, &builder, status);
		}

		if(status == false) return status;
		vb = builder.wrapUp(next);

		//vb->print(std::cout);

		analyzer.LoopBoundAnalyzer(vb, reg, off);
		if(reg == 0 && off == 0) status = false;
		//printf("entry = 0x%x, bound reg = s%d, off = 0x%x\n", entry, reg, off);

		return status;
	}

	void IA32NestedLoopDetector::i_jmp_nest(x86_insn *insn, x86_operand *op, XTMemAddr cur)
	{
		// only get the first nested loop now
		// should get all nested loops in the future
		if(op->op1.imm >= start && op->op1.imm < cur && nested_loops.size() == 0)
		//if(op->op1.imm >= start && op->op1.imm < cur)
		{
			//printf("loop detected: cur = 0x%x, imm = 0x%x\n", cur, op->op1.imm);
			if(op->op1.imm > last_exit)		// detected one nested loop: last_entry ~ last_exit
			{
			printf("nested loop: entry = 0x%x, exit = 0x%x, level = %d\n", last_entry, last_exit, nested_count);
				bool flag = true;
				std::vector<LOOP_BASIC>::iterator vit = cur_nest.begin();
				while(vit != cur_nest.end())
				{
					if( (*vit).entry < last_entry ) { flag = false; break; }
					vit++;
				}

				if( (nested_count > 1) && flag)
				{
					cur_nest.pop_back();	// delete the outmost loop in cur_nests
					NESTED_LOOP tmp_nest;
					RegNum reg;
					XTInt32 off;
					// the outmost loop
					if(__getBoundInfo(last_entry, reg, off))
					{
						LOOP_INFO info(last_entry, last_exit, reg, off);
						tmp_nest.outmost = info;
					}
					// handle the inner loops
					vit = cur_nest.begin();
					while(vit != cur_nest.end())
					{
						if(__getBoundInfo((*vit).entry, reg, off))
						{
							LOOP_INFO inner_info((*vit).entry, (*vit).exit, reg, off);
							tmp_nest.inners.push_back(inner_info);
						}
						vit++;
					}
					nested_loops.push_back(tmp_nest);
				}
				cur_nest.clear();
				cur_nest.push_back(LOOP_BASIC(op->op1.imm, cur));		// push_back current for the next nested loop
				nested_count = 1;
			}
			else
			{
				cur_nest.push_back(LOOP_BASIC(op->op1.imm, cur));
				nested_count++;
			}
			last_exit = cur;
			last_entry = op->op1.imm;
		}
	}

	void IA32NestedLoopDetector::run()
	{
		MemAddr next = start;
		while(next < end)
		{
			next = decode(next);
		}
	}

	XTMemAddr IA32NestedLoopDetector::decode(XTMemAddr pc)
	{
		MemAddr next = pc;		// initialize next pc address
		MemAddr at = pc; 		// locate the code address in memory image

		disassembler disasm;
		disasm.set_syntax_intel();

		struct x86_operand op;
		NestDecodePtr_t cb;

		struct x86_insn insn(1, 0);

		insn = disasm.decode(1, 0, (bx_address)0, (bx_address)next, (Bit8u*)at, &cb, &op);

		if( cb != NULL ) { (this->*cb)(&insn, &op, next);
			next += insn.ilen; 
		}
		else {
			next += insn.ilen;
		}

		return next;
	}

	XTInt32 IA32NestedLoopDetector::isNestedLoop(XTMemAddr entry)
	{
		for(int i = 0; i < nested_loops.size(); i++)
		{
			if(nested_loops[i].outmost.exit == entry) {
				printf("it's an outmost loop! spc = 0x%x\n", entry);
				return 1;		//outmost
			}
			else
			{
				for(int j = 0; j < nested_loops[i].inners.size(); j++)
				{
					if( nested_loops[i].inners[j].exit == entry) {
						printf("it's an inner loop! spc = 0x%x\n", entry);
						return 2;	//inner
					}
				}
			}
		}
		return 0;	// not related to any nested loops
	}

	void IA32NestedLoopDetector::printNestedLoops()
	{
		for(int i = 0; i < nested_loops.size(); i++)
		{
			printf("===============NESTED_LOOPS[%d]:=================\n", i);
			printf("Outmost: entry = 0x%x, exit = 0x%x, bound: s%d:0x%x\nInners:\n", nested_loops[i].outmost.entry, nested_loops[i].outmost.exit, nested_loops[i].outmost.bound_reg, nested_loops[i].outmost.bound_off);
			for(int j = 0; j < nested_loops[i].inners.size(); j++)
			{
				printf("\tentry = 0x%x, exit = 0x%x, bound: s%d:0x%x\n", nested_loops[i].inners[j].entry, nested_loops[i].inners[j].exit, nested_loops[i].inners[j].bound_reg, nested_loops[i].inners[j].bound_off);
			}
		}
	}

	XTMemAddr IA32NestedLoopDetector::getOutmostEntry()
	{
		return nested_loops[0].outmost.entry;
	}
	XTMemAddr IA32NestedLoopDetector::getOutmostExit()
	{
		return nested_loops[0].outmost.exit;
	}
}

